#include "JobChooser.cuh"

MaxClauseJobChooser::MaxClauseJobChooser(vector <Clause> * formula, int number_of_variables,
		int number_of_dead_variables,
		int threads, int number_of_blocks):
variable_chooser(formula, number_of_variables),
JobChooser()
{
	this->number_of_working_variables = number_of_variables - number_of_dead_variables;
	this->number_of_threads = threads;
	this->number_of_blocks = number_of_blocks;
	this->chosen_variables = NULL;
	this->evaluated = false;
	this->strategy = DISTRIBUTE_JOBS_PER_THREAD;

}

MaxClauseJobChooser::MaxClauseJobChooser(vector <Clause> * formula, int number_of_variables, int number_of_dead_variables,
		int threads, int number_of_blocks, ChoosingStrategy strategy):
variable_chooser(formula, number_of_variables),
JobChooser()
{
	this->number_of_working_variables = number_of_variables - number_of_dead_variables;
	this->number_of_threads = threads;
	this->number_of_blocks = number_of_blocks;
	this->chosen_variables = NULL;
	this->evaluated = false;
	this->strategy = strategy;

}

MaxClauseJobChooser::~MaxClauseJobChooser()
{

	if(chosen_variables != NULL)
	{
		free(chosen_variables);
	}

}
bool MaxClauseJobChooser::is_evaluated()
{
	return evaluated;
}

void MaxClauseJobChooser::evaluate()
{

	evalVarPerJobs();

	chosen_variables = (Var*) malloc(sizeof(Var)*variables_per_job);

	variable_chooser.evaluate();

	for(int i = 0; i < variables_per_job; i++)
	{
#ifdef USE_ASSERTIONS
		assert(variable_chooser.has_next_variable());
#endif

		chosen_variables[i] = variable_chooser.next_variable();
	}

	evaluated = true;
}

void MaxClauseJobChooser::getJobs(JobsQueue & queue)
{
#ifdef USE_ASSERTIONS
	assert(evaluated);
#endif

	Lit* fixed_lits = (Lit*) malloc(sizeof(Lit)*variables_per_job);

	addJobs(fixed_lits, 0, queue);

}

void MaxClauseJobChooser::addJobs(Lit* fixed_lits, int number_fixed_lits, JobsQueue & queue)
{

	if(number_fixed_lits == variables_per_job)
	{
		Job j = mkJob_dev(fixed_lits, number_fixed_lits);
		queue.add(j);

		return;
	}

	Lit lit = mkLit(chosen_variables[number_fixed_lits], true);

	fixed_lits[number_fixed_lits] = lit;

	addJobs(fixed_lits, number_fixed_lits+1, queue);

	lit = mkLit(chosen_variables[number_fixed_lits], false);

	fixed_lits[number_fixed_lits] = lit;

	addJobs(fixed_lits, number_fixed_lits+1, queue);

}

int MaxClauseJobChooser::get_number_of_jobs()
{
#ifdef USE_ASSERTIONS
	assert(evaluated);
#endif

	return (int) pow(2,(float)variables_per_job);
}

void MaxClauseJobChooser::evalVarPerJobs()
{
	switch(strategy)
	{
	case DISTRIBUTE_JOBS_PER_THREAD:
		evalVarPerJobsDistribute();
		break;

	case UNIFORM:
		evalVarPerJobsUniform();

		break;
	default:
		assert(false);
		break;

	}
}

void MaxClauseJobChooser::evalVarPerJobsDistribute()
{
	int expected_number_of_jobs = (number_of_threads*number_of_blocks)*JOBS_PER_THREAD;

	long int max_number_of_jobs = (long int) pow((double)2,
			(((number_of_working_variables - MIN_FREE_VARS) > 1) ?
					number_of_working_variables - MIN_FREE_VARS : 1));


	if((number_of_working_variables - MIN_FREE_VARS) > 62)
	{
		max_number_of_jobs = (long int) pow((double)2,62);
	}

#ifdef USE_ASSERTIONS
	assert(max_number_of_jobs > 0);
#endif

	expected_number_of_jobs = expected_number_of_jobs <= max_number_of_jobs ?
			expected_number_of_jobs : max_number_of_jobs;

	float aux = log2((float) expected_number_of_jobs);

	int aux2 = (int) aux;

	variables_per_job = aux - aux2 == 0 ? aux2 : aux2 + 1 ;
	variables_per_job = variables_per_job < MAX_VARS ? variables_per_job : MAX_VARS;

}

void MaxClauseJobChooser::evalVarPerJobsUniform()
{
	int expected_number_of_variables = UNIFORM_NUMBER_OF_VARS;
	int max_number_of_variables = ((number_of_working_variables - MIN_FREE_VARS) > 1) ?
					number_of_working_variables - MIN_FREE_VARS : 1;

	variables_per_job = expected_number_of_variables < max_number_of_variables ?
			expected_number_of_variables : max_number_of_variables;
	variables_per_job = variables_per_job < MAX_VARS ? variables_per_job : MAX_VARS;

}
